package network.string.communication;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

import org.anddev.andengine.extension.multiplayer.protocol.adt.message.IMessage;
import org.anddev.andengine.extension.multiplayer.protocol.adt.message.client.ClientMessage;
import org.anddev.andengine.extension.multiplayer.protocol.adt.message.client.IClientMessage;
import org.anddev.andengine.extension.multiplayer.protocol.adt.message.server.IServerMessage;
import org.anddev.andengine.extension.multiplayer.protocol.adt.message.server.ServerMessage;
import org.anddev.andengine.extension.multiplayer.protocol.client.IServerMessageHandler;
import org.anddev.andengine.extension.multiplayer.protocol.client.connector.ServerConnector;
import org.anddev.andengine.extension.multiplayer.protocol.client.connector.SocketConnectionServerConnector;
import org.anddev.andengine.extension.multiplayer.protocol.client.connector.SocketConnectionServerConnector.ISocketConnectionServerConnectorListener;
import org.anddev.andengine.extension.multiplayer.protocol.server.IClientMessageHandler;
import org.anddev.andengine.extension.multiplayer.protocol.server.SocketServer;
import org.anddev.andengine.extension.multiplayer.protocol.server.SocketServer.ISocketServerListener;
import org.anddev.andengine.extension.multiplayer.protocol.server.connector.ClientConnector;
import org.anddev.andengine.extension.multiplayer.protocol.server.connector.SocketConnectionClientConnector;
import org.anddev.andengine.extension.multiplayer.protocol.server.connector.SocketConnectionClientConnector.ISocketConnectionClientConnectorListener;
import org.anddev.andengine.extension.multiplayer.protocol.shared.SocketConnection;
import org.anddev.andengine.extension.multiplayer.protocol.util.MessagePool;
import org.anddev.andengine.util.Debug;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

public class CommunicationManager {
	
	private static final short FLAG_STRING_MESSAGE = 1;	
	private static final short FLAG_BITMAP_MESSAGE = FLAG_STRING_MESSAGE+1;	
	private SocketServer<SocketConnectionClientConnector> mSocketServer;
	private ServerConnector<SocketConnection> mServerConnector;
	private final MessagePool<IMessage> mMessagePool = new MessagePool<IMessage>();
	private CommunicationListener _communicationListener;	
	private static final int SERVER_PORT = 4444;
	
	public boolean getIsServer()
	{
		return this.isServer;
	}
	
	public static class ServerStringMessage extends ServerMessage 
	{
		public String _message;

		public ServerStringMessage() {

		}

		public ServerStringMessage(final String s) {
			_message = s;
		}

		@Override
		public short getFlag() {
			// TODO Auto-generated method stub
			return FLAG_STRING_MESSAGE;
		}

		@Override
		protected void onReadTransmissionData(DataInputStream pDataInputStream)
				throws IOException {
			_message = pDataInputStream.readLine();
	
		}

		@Override
		protected void onWriteTransmissionData(final DataOutputStream pDataOutputStream) throws IOException {		
			pDataOutputStream.write((_message+"\n").getBytes());			
		}
	}
	
	public static class ServerBitmapMessage extends ServerMessage 
	{
		public Bitmap _bitmap;
		public String _name;

		public ServerBitmapMessage() {

		}

		public ServerBitmapMessage(final String s,final String name) {
			_bitmap = BitmapFactory.decodeFile(s);
			_name = name;
		}
		
		public ServerBitmapMessage(final Bitmap b,final String name) {
			_bitmap = b;
			_name = name;
		}

		@Override
		public short getFlag() {
			// TODO Auto-generated method stub
			return FLAG_BITMAP_MESSAGE;
		}

		@Override
		protected void onReadTransmissionData(DataInputStream pDataInputStream)
				throws IOException {
			
			 _name = pDataInputStream.readLine();	
			 int size = pDataInputStream.readInt();			 
			 byte[] array = new byte[size];	
			 
			 for(int i=0;i<size;i++)
			 {
				 array[i]=pDataInputStream.readByte();
			 }
			 
			_bitmap = BitmapFactory.decodeByteArray(array,0,array.length);
	
		}

		@Override
		protected void onWriteTransmissionData(final DataOutputStream pDataOutputStream) throws IOException {		
			
			ByteArrayOutputStream stream = new ByteArrayOutputStream();	
			_bitmap.compress(Bitmap.CompressFormat.PNG,100,stream);
			byte[] byteArray = stream.toByteArray();
			
			pDataOutputStream.write((_name+"\n").getBytes());
			pDataOutputStream.writeInt(byteArray.length);
			pDataOutputStream.write(byteArray);						
			
			//pDataOutputStream.writeInt(_message+"\n");			
		}
	}
	
	public static class ClientStringMessage extends ClientMessage 
	{
		public String _message;	

		public ClientStringMessage() {

		}

		public ClientStringMessage(final String message) {
			_message = message;
		}

		@Override
		public short getFlag() {
			// TODO Auto-generated method stub
			return FLAG_STRING_MESSAGE;
		}

		@Override
		protected void onReadTransmissionData(DataInputStream pDataInputStream)
				throws IOException {
			// TODO Auto-generated method stub

			_message = pDataInputStream.readLine();

		}

		@Override
		protected void onWriteTransmissionData(
				DataOutputStream pDataOutputStream) throws IOException {
			// TODO Auto-generated method stub
			pDataOutputStream.write((_message+"\n").getBytes());
		}		
	}
	
	public static class ClientBitmapMessage extends ClientMessage 
	{
		public Bitmap _bitmap;
		public String _name;

		public ClientBitmapMessage() {

		}

		public ClientBitmapMessage(final String s,final String name) {
			_bitmap = BitmapFactory.decodeFile(s);
			_name = name;
		}

		public ClientBitmapMessage(final Bitmap b,final String name) {
			_bitmap = b;
			_name = name;
		}
		
		@Override
		public short getFlag() {
			// TODO Auto-generated method stub
			return FLAG_BITMAP_MESSAGE;
		}

		@Override
		protected void onReadTransmissionData(DataInputStream pDataInputStream)
				throws IOException {
			// TODO Auto-generated method stub
			 _name = pDataInputStream.readLine();
			 int size = pDataInputStream.readInt();			 
			 byte[] array = new byte[size];	
			 
			 for(int i=0;i<size;i++)
			 {
				 array[i]=pDataInputStream.readByte();
			 }
			 
			_bitmap = BitmapFactory.decodeByteArray(array,0,array.length);

		}

		@Override
		protected void onWriteTransmissionData(
				DataOutputStream pDataOutputStream) throws IOException {
			// TODO Auto-generated method stub
			
			
			
			ByteArrayOutputStream stream = new ByteArrayOutputStream();	
			_bitmap.compress(Bitmap.CompressFormat.PNG,100,stream);
			byte[] byteArray = stream.toByteArray();
			
			pDataOutputStream.write((_name+"\n").getBytes());
			pDataOutputStream.writeInt(byteArray.length);
			pDataOutputStream.write(byteArray);				
		}		
	}
	
	private void initMessagePool() {
		this.mMessagePool.registerMessage(FLAG_STRING_MESSAGE, ServerStringMessage.class);
		this.mMessagePool.registerMessage(FLAG_BITMAP_MESSAGE, ServerBitmapMessage.class);
	}
	
	private void initServer() {
		this.mSocketServer = new SocketServer<SocketConnectionClientConnector>(SERVER_PORT, new ExampleClientConnectorListener(), new ExampleServerStateListener()) {
			@Override
			protected SocketConnectionClientConnector newClientConnector(final SocketConnection pSocketConnection) throws IOException {

				final SocketConnectionClientConnector clientConnector = new SocketConnectionClientConnector(pSocketConnection);

				clientConnector.registerClientMessage(FLAG_STRING_MESSAGE, ClientStringMessage.class, new IClientMessageHandler<SocketConnection>() {
					@Override
					public void onHandleMessage(final ClientConnector<SocketConnection> pClientConnector, final IClientMessage pClientMessage) throws IOException {										
						_communicationListener.StringReceiveEvent(((ClientStringMessage)pClientMessage)._message);
					}
				});
				
				clientConnector.registerClientMessage(FLAG_BITMAP_MESSAGE, ClientBitmapMessage.class, new IClientMessageHandler<SocketConnection>() {
					@Override
					public void onHandleMessage(final ClientConnector<SocketConnection> pClientConnector, final IClientMessage pClientMessage) throws IOException {										
						_communicationListener.BitmapReceiveEvent(((ClientBitmapMessage)pClientMessage)._bitmap,((ClientBitmapMessage)pClientMessage)._name);
					}
				});
				
				return clientConnector;
			}
		};

		this.mSocketServer.start();
	}
	
	
	private void initClient(String ip) {
		try {
			this.mServerConnector = new SocketConnectionServerConnector(new SocketConnection(new Socket(ip, SERVER_PORT)), new ExampleServerConnectorListener());

			this.mServerConnector.registerServerMessage(FLAG_STRING_MESSAGE, ServerStringMessage.class, new IServerMessageHandler<SocketConnection>() {
				@Override	
				public void onHandleMessage(final ServerConnector<SocketConnection> pServerConnector, final IServerMessage pServerMessage) throws IOException {
					_communicationListener.StringReceiveEvent(((ServerStringMessage)pServerMessage)._message);
				}
			});
			
			this.mServerConnector.registerServerMessage(FLAG_BITMAP_MESSAGE, ServerBitmapMessage.class, new IServerMessageHandler<SocketConnection>() {
				@Override
				public void onHandleMessage(final ServerConnector<SocketConnection> pServerConnector, final IServerMessage pServerMessage) throws IOException {
					_communicationListener.BitmapReceiveEvent(((ServerBitmapMessage)pServerMessage)._bitmap,((ServerBitmapMessage)pServerMessage)._name);
				}
			});

			this.mServerConnector.getConnection().start();
		} catch (final Throwable t) {
			Debug.e(t);
		}
	}
	
	private class ExampleServerStateListener implements ISocketServerListener<SocketConnectionClientConnector> {
		@Override
		public void onStarted(final SocketServer<SocketConnectionClientConnector> pSocketServer) 
		{
			isServer = true;	
			Log.d("test","1");
		}

		@Override
		public void onTerminated(final SocketServer<SocketConnectionClientConnector> pSocketServer) 
		{
			isServer = false;
			Log.d("test","2");
		}

		@Override
		public void onException(final SocketServer<SocketConnectionClientConnector> pSocketServer, final Throwable pThrowable) {
			Log.d("test","3");
		}
	}

	private class ExampleClientConnectorListener implements ISocketConnectionClientConnectorListener {
		@Override
		public void onStarted(final ClientConnector<SocketConnection> pConnector) 
		{
			_communicationListener.ConnectSuccessEvent();
			Log.d("test","4");
		}

		@Override
		public void onTerminated(final ClientConnector<SocketConnection> pConnector) 
		{
			_communicationListener.ConnectLostEvent();
			Log.d("test","5");
		}
	}
	
	private class ExampleServerConnectorListener implements ISocketConnectionServerConnectorListener {
		@Override
		public void onStarted(final ServerConnector<SocketConnection> pConnector) {
			isClient = true;
			_communicationListener.ConnectSuccessEvent();
			Log.d("test","6");
		}

		@Override
		public void onTerminated(final ServerConnector<SocketConnection> pConnector) {
			isClient = false;
			_communicationListener.ConnectLostEvent();
			Log.d("test","7");
		}
	}
	
	public CommunicationManager(CommunicationListener communicationListener)
	{
		_communicationListener = communicationListener;
		initMessagePool();
	}
	
	public void openServer() throws Exception
	{
		if(!isServer&&!isClient)
		{
		initServer();
		}
	}
	
	public void openClient(String ip) throws Exception
	{
		if(!isServer&&!isClient)
		{
		initClient(ip);
		}
	}
	
	boolean isServer = false;
	boolean isClient = false;
	
	public void close()
	{
		if(mSocketServer!=null)
		{
			mSocketServer.terminate();
			mSocketServer = null;
		}
		
		if(mServerConnector!=null)
		{
			mServerConnector.terminate();
			mSocketServer = null;
		}
		
		isServer = false;
		isClient = false;
	}
	
	public void sendMessage(String message) throws Exception
	{
		if(message.contains("\n"))throw new Exception("no \\n allow");
		
		if(isServer)
		{
			try {
				mSocketServer.sendBroadcastServerMessage(new ServerStringMessage(message));
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
		else if(isClient)
		{
			try {
				mServerConnector.sendClientMessage(new ClientStringMessage(message));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}		
	}
	
	public void sendBitmap(String s,String name)
	{
		if(isServer)
		{
			try {
				mSocketServer.sendBroadcastServerMessage(new ServerBitmapMessage(s,name));
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
		else if(isClient)
		{
			try {
				mServerConnector.sendClientMessage(new ClientBitmapMessage(s,name));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}		
	}
	
	public void sendBitmap(Bitmap bitmap,String name) throws Exception
	{
		
		if(isServer)
		{
			try {
				mSocketServer.sendBroadcastServerMessage(new ServerBitmapMessage(bitmap,name));
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
		else if(isClient)
		{
			try {
				mServerConnector.sendClientMessage(new ClientBitmapMessage(bitmap,name));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}		
	}
}
